package com.puzzlebazar.client.util;

import java.util.Iterator;
import java.util.Vector;

import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

public class ConnectionLock {

	private final HorizontalPanel message;
	private int lockCount = 0;
	private final Vector<Tool> controlledTools = new Vector<Tool>();
	private final Vector<UnlockCallback> unlockCallbacks = new Vector<UnlockCallback>();
	private final Vector<UnlockCallback> oneTimeCallbacks = new Vector<UnlockCallback>();
	private boolean globalLock = false;
	private boolean whiteScreen = false;
	
	/**
	 * Create a connection lock that does not display a locking message
	 */
	public ConnectionLock() {
		this.message = null;
	}	
	
	/**
	 * Create a connection lock that display a locking message at the top of
	 * the browser window
	 */
	public ConnectionLock( String message ) {

		Label label = new Label(message);
//		DOM.setStyleAttribute(label.getElement(), "position", "fixed");
		label.setStylePrimaryName( "connectionLock" );
		label.setHorizontalAlignment( HasHorizontalAlignment.ALIGN_CENTER );
		
		this.message = new HorizontalPanel();
		this.message.setHorizontalAlignment( HasHorizontalAlignment.ALIGN_CENTER );		
		this.message.add( label );			
	}
	
	/**
	 * Enable or disable complete interaction locking 
	 * behavior (glass window)
	 * 
	 * @param globalLock True to enable global locking, false to disable it
	 */
	public void setGlobalLock( boolean globalLock ) {
		assert( !locked() );
		this.globalLock = globalLock;
		if( globalLock == false )
			whiteScreen = false;
	}	

	/**
	 * Enable or disable complete interaction locking 
	 * behavior and screen whiteout (glass window)
	 * 
	 * @param globalLock True to enable global locking, false to disable it
	 */
	public void setGlobalLockAndWhiteScreen( boolean globalLock ) {
		assert( !locked() );
		this.globalLock = globalLock; 
		this.whiteScreen = globalLock; 
	}	
	
	/**
	 * Adds a tool that will be disabled then the lock is active and enabled when
	 * the lock is inactive
	 * 
	 * @param tool Tool to control with this lock
	 */
	public void addControlledTool( Tool tool ) {
		controlledTools.add( tool );
	}

	/**
	 * Adds a callback that will be called whenever the lock is entirely released
	 * 
	 * @param callback Callback to add to this lock
	 */
	public void addUnlockCallback( UnlockCallback callback ) {
		unlockCallbacks.add( callback );
	}

	/**
	 * Adds a callback that will be called whenever the lock is entirely released
	 * This callback will be called once then taken off the callback list
	 * 
	 * @param callback Callback to add to this lock
	 */
	public void addOneTimeUnlockCallback( UnlockCallback callback ) {
		oneTimeCallbacks.add( callback );
	}
	
	/**
	 * Called whenever a connection takes a lock on this object
	 * Locks are not mutually exclusive, multiple different connections can 
	 * hold a lock. Each connection is responsible of releasing it.
	 */
	public void grab() {
		if( lockCount == 0 )
			lock();
		lockCount++;		
	}
	

	/**
	 * Called whenever a connection releases a lock from this object
	 */
	public void release() {
		if( lockCount == 1 )
			unlock();
		lockCount--;		
	}

	/**
	 * Performs everything that needs to be done when locking
	 */
	private void lock() {
		for (Iterator<Tool> iterator = controlledTools.iterator(); iterator.hasNext();) {
			Tool tool = iterator.next();
			tool.setEnabled( false );			
		}
		if( globalLock ) GlassWindow.showGlassWindow(whiteScreen);
		if( message != null ) showMessage();
	}

	/**
	 * Performs everything that needs to be done when unlocking
	 */
	private void unlock() {
		if( message != null ) hideMessage();
		for (Iterator<Tool> iterator = controlledTools.iterator(); iterator.hasNext();) {
			Tool tool = iterator.next();
			tool.setEnabled( true );			
		}
		for (Iterator<UnlockCallback> iterator = unlockCallbacks.iterator(); iterator.hasNext();) {
			UnlockCallback callback = iterator.next();
			callback.unlocked();			
		}
		for (Iterator<UnlockCallback> iterator = oneTimeCallbacks.iterator(); iterator.hasNext();) {
			UnlockCallback callback = iterator.next();
			callback.unlocked();
		}
		oneTimeCallbacks.clear();
		if( globalLock ) GlassWindow.hideGlassWindow();
	}

	/**
	 * Checks whether at least one connection holds a lock on this object
	 * 
	 * @return True if the object is locked, false otherwise
	 */
	public boolean locked() {
		return lockCount != 0;
	}

	/**
	 * Show the locking message at the top of the browser window
	 */
	private void showMessage() {
		RootPanel panel = RootPanel.get();
		panel.add( message, 0, 0 );
		message.setWidth( panel.getOffsetWidth() + "px" );		
	}

	/**
	 * Hide the locking message from the top of the browser window
	 */
	private void hideMessage() {
		RootPanel panel = RootPanel.get();
		panel.remove( message );
	}
	
	public interface UnlockCallback {
		/**
		 * Called whenever the lock is entirely released
		 */
		public void unlocked();
	}


	  
}
